package com.google.zxing.client.android.came2;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.ImageFormat;

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.SurfaceHolder;

import com.google.zxing.PlanarYUVLuminanceSource;


import java.util.Arrays;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class CameraDeviceManager {
    private String cameraId;
    private ImageReader imageReader;
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);
    private CameraCaptureSession mSession;
    private HandlerThread mBackgroudThread;
    private Handler mBackgroundHandler;

    private final Context mContext;
    private CameraDevice mDevice;
    private SurfaceHolder mHolder;
    private boolean previewing = false;
    CaptureRequest.Builder mPreviewRequestBuilder;
    private ImagePreviewCallback previewCallback;
    @SuppressWarnings("NewApi")
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            Log.d("tag","CamreaDevice onOpened");
            mCameraOpenCloseLock.release();
            mDevice = camera;

        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            mCameraOpenCloseLock.release();
            mDevice.close();
            mDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            mCameraOpenCloseLock.release();
            mDevice.close();
            mDevice = null;
        }
    };

    public CameraDeviceManager(Context mContext) {
        this.mContext = mContext;
        previewCallback = new ImagePreviewCallback();
    }

    public synchronized void openDriver(SurfaceHolder holder){
        mHolder = holder;
        startBackgroundThread();
        openCamera();
    }

    public synchronized void closeDriver(){
        closeCamera();

        stopPreview();
        stopBackgroundThread();
    }

    public boolean isopen(){
        return mDevice != null;
    }

    public synchronized void startPreview(){
        if (!previewing){
            previewing = true;
            createCameraPreviewSession();

        }
    }

    @SuppressLint("NewApi")
    public synchronized void stopPreview(){
        if (mDevice != null && previewing) {
            try {
                mSession.abortCaptures();
                mSession.stopRepeating();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
            previewCallback.setHandler(null, 0);
            previewing = false;
        }
    }

    @SuppressLint("NewApi")
    private void createCameraPreviewSession(){
        try {
            mPreviewRequestBuilder
                    =mDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(mHolder.getSurface());
            mPreviewRequestBuilder.addTarget(imageReader.getSurface());
            mDevice.createCaptureSession(Arrays.asList(mHolder.getSurface(),imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    mSession = session;
                    try {
                        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

                        CaptureRequest mPreviewRequest = mPreviewRequestBuilder.build();
                        mSession.setRepeatingRequest(mPreviewRequest,
                                null, mBackgroundHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {

                }
            },mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @SuppressLint({"NewApi", "MissingPermission"})
    private void openCamera(){
        CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);

        try {
            for (String cameraId : cameraManager.getCameraIdList()){
                CameraCharacteristics characteristics
                        = cameraManager.getCameraCharacteristics(cameraId);

                // We don't use a front facing camera in this sample.
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
                    continue;
                }

                imageReader = ImageReader.newInstance(1080,2340, ImageFormat.YUV_420_888,3);
                imageReader.setOnImageAvailableListener(previewCallback,mBackgroundHandler);
                this.cameraId = cameraId;
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)){
                throw new RuntimeException("Time out");
            }

            cameraManager.openCamera(cameraId,mStateCallback,mBackgroundHandler);
        }catch (CameraAccessException e){

        }catch (InterruptedException e){}
    }

    @SuppressLint("NewApi")
    private void closeCamera(){
        try {
            mCameraOpenCloseLock.acquire();
            if (null != mSession) {
                mSession.close();
                mSession = null;
            }
            if (null != mDevice) {
                mDevice.close();
                mDevice = null;
            }
            if (null != imageReader) {
                imageReader.close();
                imageReader = null;
            }

        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        } finally {
            mCameraOpenCloseLock.release();
        }

    }

    private void startBackgroundThread(){
        mBackgroudThread = new HandlerThread("camera");
        mBackgroudThread.start();
        mBackgroundHandler = new Handler(mBackgroudThread.getLooper());
    }

    private void stopBackgroundThread(){
        mBackgroudThread.quitSafely();
        try {
            mBackgroudThread.join();
            mBackgroudThread = null;
            mBackgroundHandler = null;
        }catch (InterruptedException e){}
    }

    public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
        return new PlanarYUVLuminanceSource(data, width, height, 0, 0,
                width, height, false);
    }

    public synchronized void requestPreviewFrame(Handler handler, int message) {
        if (mDevice != null && previewing) {
            previewCallback.setHandler(handler, message);
        }
    }
}
